﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GingerBeard.Utilities
{
	public class Vector
	{
		float[] v = new float[4];

		public Vector()
		{

		}

		public Vector(float x, float y, float z)
		{
			v[0] = x; v[1] = y; v[2] = z; v[3] = 0;
		}

		public Vector(float x, float y, float z, float w)
		{
			v[0] = x; v[1] = y; v[2] = z; v[3] = w;
		}

		public void Set(float x, float y, float z, float w)
		{
			v[0] = x;
			v[1] = y;
			v[2] = z;
			v[3] = w;
		}

		public void Set(Vector b)
		{
			v[0] = b.Get(0);
			v[1] = b.Get(1);
			v[2] = b.Get(2);
			v[3] = b.Get(3);
		}

		public float Get(int index)
		{
			return v[index];
		}

		public float Set(int index, float val)
		{
			return v[index] = val;
		}

		public Vector GetXYZ_W0()
		{
			return new Vector(v[0], v[1], v[2], 0);
		}

		public Vector GetXYZ_W1()
		{
			return new Vector(v[0], v[1], v[2], 1);
		}

		public static Vector operator +(Vector a, Vector b)
		{
			return a.Add(b);
		}
		
		public static Vector operator -(Vector a, Vector b)
		{
			return a.Sub(b);
		}

		public static Vector operator *(Vector a, Vector b)
		{
			return a.Mul(b);
		}

		public static Vector operator /(Vector a, Vector b)
		{
			return new Vector(a.Get(0) / b.Get(0), a.Get(1) / b.Get(1), a.Get(2) / b.Get(2), a.Get(3) / b.Get(3));
		}

		public static Vector operator -(Vector b)
		{
			return new Vector(- b.Get(0), - b.Get(1), - b.Get(2),- b.Get(3));
		}

		public Vector Abs()
		{
			return new Vector(Math.Abs(Get(0)), Math.Abs(Get(1)), Math.Abs(Get(2)), Math.Abs(Get(3)));
		}

		public Vector Inv()
		{
			return new Vector(1.0f/Get(0), 1.0f/Get(1), 1.0f/Get(2), 1.0f/Get(3));
		}

		public Vector Max(Vector b)
		{
			return new Vector(Math.Max(Get(0), b.Get(0)), Math.Max(Get(1), b.Get(1)), Math.Max(Get(2), b.Get(2)), Math.Max(Get(3), b.Get(3)));
		}

		public Vector Sign()
		{
			return new Vector(Math.Sign(Get(0)), Math.Sign(Get(1)), Math.Sign(Get(2)), Math.Sign(Get(3)));
		}

		public Vector MulX(Vector b)
		{
			return new Vector(Get(0) * b.Get(0), Get(1) * b.Get(0), Get(2) * b.Get(0), Get(3) * b.Get(0));
		}

		public Vector MulY(Vector b)
		{
			return new Vector(Get(0) * b.Get(1), Get(1) * b.Get(1), Get(2) * b.Get(1), Get(3) * b.Get(1));
		}

		public Vector MulZ(Vector b)
		{
			return new Vector(Get(0) * b.Get(2), Get(1) * b.Get(2), Get(2) * b.Get(2), Get(3) * b.Get(2));
		}

		public Vector MulW(Vector b)
		{
			return new Vector(Get(0) * b.Get(3), Get(1) * b.Get(3), Get(2) * b.Get(3), Get(3) * b.Get(3));
		}

		public Vector Offset(float v)
		{
			return new Vector(Get(0) + v, Get(1) + v, Get(2) + v, Get(3) + v);
		}

		public Vector Scale(float v)
		{
			return new Vector(Get(0) * v, Get(1) * v, Get(2) * v, Get(3) * v);
		}

		public Vector Add(Vector b)
		{
			return new Vector(Get(0) + b.Get(0), Get(1) + b.Get(1), Get(2) + b.Get(2), Get(3) + b.Get(3));
		}

		public Vector Sub(Vector b)
		{
			return new Vector(Get(0) - b.Get(0), Get(1) - b.Get(1), Get(2) - b.Get(2), Get(3) - b.Get(3));
		}

		public Vector Mul(Vector b)
		{
			return new Vector(Get(0) * b.Get(0), Get(1) * b.Get(1), Get(2) * b.Get(2), Get(3) * b.Get(3));
		}

		public float Dot(Vector b)
		{
			return (Get(0) * b.Get(0)) + (Get(1) * b.Get(1)) + (Get(2) * b.Get(2)) + (Get(3) * b.Get(3));
		}

		public Vector Cross(Vector b)
		{
			float x1 = Get(0);
			float y1 = Get(1);
			float z1 = Get(2);
			float x2 = b.Get(0);
			float y2 = b.Get(1);
			float z2 = b.Get(2);

			return new Vector(y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2);
		}

		public float Length()
		{
			return (float)Math.Sqrt(this.Dot(this));
		}

		public float LengthSquared()
		{
			return this.Dot(this);
		}

		public void Normalise()
		{
			float scale = 1.0f / this.Length();
			Set(0, Get(0) * scale);
			Set(1, Get(1) * scale);
			Set(2, Get(2) * scale);
			Set(3, Get(3) * scale);
		}

		public Vector Normalised()
		{
			return this.Scale(1.0f / this.Length());
		}

		public static Vector XAxis = new Vector(1, 0, 0, 1);
		public static Vector YAxis = new Vector(0, 1, 0, 1);
		public static Vector ZAxis = new Vector(0, 0, 1, 1);
		public static Vector WAxis = new Vector(0, 0, 0, 1);

		public static Vector XZAxis = new Vector(1, 0, 1, 1);
		public static Vector YZAxis = new Vector(0, 1, 1, 1);
		public static Vector XYAxis = new Vector(1, 1, 0, 1);

		public static Vector NegXAxis = new Vector(-1, 1, 1, 1);
		public static Vector NegYAxis = new Vector(1, -1, 1, 1);
		public static Vector NegZAxis = new Vector(1, 1, -1, 1);

		public static Vector One = new Vector(1, 1, 1, 1);
		public static Vector Zero = new Vector(0, 0, 0, 0);

		public static Vector FloatMax = new Vector(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue);
		public static Vector FloatMin = new Vector(float.MinValue, float.MinValue, float.MinValue, float.MinValue);
	}
}
